from __future__ import annotations

from fastapi import FastAPI, Request, HTTPException, Depends
from fastapi.responses import JSONResponse
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
from jose import jwt, JWTError
from sqlalchemy.orm import Session
from datetime import datetime
import time
import json
from typing import Optional

from config import SECRET_KEY, ALGORITHM
from middlewares.exceptions import UnauthenticatedException
from schemas.database import SessionLocal
from schemas import crud, models
from logs.log import logger

security = HTTPBearer()


def get_db():
    db = SessionLocal()
    try:
        yield db
    finally:
        db.close()


class LoggingMiddleware:
    def __init__(self, app: FastAPI):
        self.app = app

    async def __call__(self, scope, receive, send):
        if scope['type'] != 'http':
            return await self.app(scope, receive, send)

        request = Request(scope=scope)
        start_time = time.time()

        # 创建一个新的发送函数，用来捕获响应
        async def send_wrapper(message):
            process_time = time.time() - start_time
            if message['type'] == 'http.response.start':
                status_code = message.get('status', 500)
                await self.log_request(request, receive, status_code, process_time)
            await send(message)  # 使用原始的send函数

        await self.app(scope, receive, send_wrapper)

    async def log_request(self, request: Request, receive, status_code: int, process_time: float):
        method = request.method
        url = str(request.url)
        headers = dict(request.headers)
        
        # 读取请求体
        body = b''
        async def custom_receive():
            nonlocal body
            message = await receive()
            if message['type'] == 'http.request':
                body = message.get('body', b'') or message.get('streamed_body', b'')
            return message
        
        # 使用自定义的接收函数来读取请求体
        request.scope['receive'] = custom_receive
        
        # 读取请求体
        body_str = '<binary data>'
        try:
            body_str = body.decode('utf-8')
        except UnicodeDecodeError:
            pass
        
        # 记录日志到文件
        log_entry = f"{datetime.now()} - Method: {method}, URL: {url}, Headers: {headers}, Body: {body_str}, Status: {status_code}, Process Time: {process_time:.4f}s\n"
        logger.info(log_entry)

        # 获取用户ID
        user_id = None
        try:
            auth_header = headers.get('Authorization')
            if auth_header and auth_header.startswith('Bearer '):
                token = auth_header.split(' ')[1]
                payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
                username = payload.get("sub")
                if username:
                    db = next(get_db())
                    user = crud.get_user_by_username(db, username=username)
                    if user:
                        user_id = user.id
        except JWTError:
            pass  # 如果token解析失败，不记录用户ID

        # 记录日志到数据库
        if user_id:
            db = next(get_db())
            api_log = models.APILog(
                endpoint=url,
                method=method,
                params=json.dumps({"headers": headers, "body": body_str}),
                status_code=status_code,
                response_time=process_time,
                user_id=user_id,
                error_message=None if status_code < 400 else "Error occurred"
            )
            db.add(api_log)
            db.commit()
        else:
            db = next(get_db())
            api_log = models.APILog(
                endpoint=url,
                method=method,
                params=json.dumps({"headers": headers, "body": body_str}),
                status_code=status_code,
                response_time=process_time,
                user_id=None,
                error_message=None if status_code < 400 else "Error occurred"
            )
            db.add(api_log)
            db.commit()

async def get_current_user(credentials: HTTPAuthorizationCredentials = Depends(security), db: Session = Depends(get_db)) -> Optional[
    models.User]:
    try:
        payload = jwt.decode(credentials.credentials, SECRET_KEY, algorithms=[ALGORITHM])
        username: str = payload.get("sub")
        if username is None:
            raise UnauthenticatedException(status_code=401, detail="无效的认证凭证")
    except JWTError:
        print("无效的认证凭证")
        logger.error("无效的认证凭证")
        raise UnauthenticatedException(status_code=401, detail="无效的认证凭证")
    
    user = crud.get_user_by_username(db, username=username)
    if user is None:
        raise UnauthenticatedException(status_code=401, detail="用户不存在")
    return user


def admin_required(current_user: models.User = Depends(get_current_user)):
    if not current_user.is_admin:
        raise UnauthenticatedException(status_code=403, detail="需要管理员权限")
    return current_user

def login_required(current_user: models.User = Depends(get_current_user)):
    if not current_user.is_active:
        raise UnauthenticatedException(status_code=403, detail="用户账号未激活")
    return current_user

class ResponseModel:
    status: int
    data: dict | list | None
    message: str
# 生成响应代码

def generate_response(data, status_code=200, message="成功"):
    if status_code == 401 or status_code == 403:
        status = "unauthorized"
    elif status_code > 400:
        status = "error"
    else:
        status = "success"
    try:
        return JSONResponse(
            content={
                "status": status,
                "message": message,
                "data": data
            },
            status_code=status_code
        )
    except TypeError as e:
        return {
            "status": status,
            "message": message,
            "data": data
        }
# 使用示例：
# @get("/admin_only", dependencies=[Depends(admin_required)])
# async def admin_only_route():
#     return {"message": "只有管理员可以访问此路由"}

# @get("/user_only", dependencies=[Depends(login_required)])
# async def user_only_route():
#     return {"message": "只有登录用户可以访问此路由"}

# @get("/public")
# async def public_route():
#     return {"message": "所有人都可以访问此路由"}

# 在主应用中添加中间件：
# middleware("http")(log_request)
